package com.jswdwsx.esign.service

import com.jswdwsx.esign.exception.EsignBusinessException
import com.jswdwsx.esign.commom.ResEnum
import com.jswdwsx.esign.extension.toDateTimeString
import com.jswdwsx.esign.protocol.EsignOtherApi
import com.jswdwsx.esign.protocol.Token
import com.jswdwsx.esign.protocol.response.TokenResp
import com.jswdwsx.esign.util.RedisUtil
import org.slf4j.LoggerFactory.getLogger
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service

/**
 * 向E签宝获取并存储token在redis中
 */
@Service
class TokenService(
    private val redisUtil: RedisUtil,
    private val esignOtherApi: EsignOtherApi
) {

    private val logger = getLogger(TokenService::class.java)

    /**
     * 调用E签宝接口获取token
     */
    fun getEsignToken(projectId: String, projectSecret: String): TokenResp {
        var tokenResp: TokenResp? = null
        var timesCount = 1
        while (tokenResp == null && timesCount < 11) {
            try {
                tokenResp = esignOtherApi.getToken(projectId, projectSecret, "client_credentials").data
                if (tokenResp == null) continue
                logger.info("新的 token 是：$tokenResp")
                val nanoSeconds = tokenResp.expiresIn.toLong()
                logger.info("新 token 的过期时间：${nanoSeconds.toDateTimeString()}")
            } catch (e: Exception) {
                logger.warn("调用E签宝获取token 失败，正在重新尝试第${timesCount++}次获取")
            }
        }
        return tokenResp ?: throw EsignBusinessException(ResEnum.CallEsignTokenFail)
    }

    /**
     * 同步调用E签宝接口获取token并更新redis
     */
    @Synchronized
    fun getToken(projectId: String, projectSecret: String): Token {
        getEsignToken(projectId, projectSecret).let {
            Token(it.token, it.expiresIn.toLong(), it.refreshToken)
        }.let {
            redisUtil.hset(StoredTokenService.ESIGN_TOKEN_KEY, projectId, it)
            redisUtil.expire(StoredTokenService.ESIGN_TOKEN_KEY, it.expiresIn)
            return it.also { logger.info("token:${it}") }
        }
    }

    @Async
    fun asyncGetToken(projectId: String, projectSecret: String) {
        getToken(projectId, projectSecret)
    }
}
